feat(search-pipeline): extract root types from the dataset registry - #698
Merged
Conversation
A dataset's own description is governed by a different application profile from the objects it contains, and it lives in the register rather than in the data a publisher ships – so a Dataset root type extracted from the distribution yields documents only for the publishers that self-describe. - add a per-stage source override to Stage: one hook substitutes the distribution for the item selector and every reader alike, so selection and extraction cannot read from different sources - honour Distribution.namedGraph in SparqlItemSelector, which only the reader applied; without it a registry-sourced stage selects the whole catalogue instead of the dataset in hand. Scope and page window are now per call, so concurrent selections cannot see each other's - add registrySource: a stage reading a fixed registry endpoint, scoped to the graph the dataset in hand names - route named root types through searchIndexerPipeline's registryTypes and the indexer's REGISTRY_ROOT_TYPES; an undeclared name fails the boot rather than shipping an empty collection
Review findings on the registry-sourced stages: - assert IRI safety in withDefaultGraph before it becomes a FROM <…> reference. A namedGraph is a plain string, typically carried in from third-party registry data; pipeline-void already guards the identical interpolation, so the reader and selector paths were the outliers - reject a chained child stage declaring sourceFor: a child reads its parent's output as its distribution, so a substituted source would discard the chain it exists to continue. Stage now reports sourcesOwnData, which the parent's constructor checks - cover the scope surviving pagination, which rewrites the query per page - hoist the registry endpoint out of the per-type map and narrow registryTypeNames to what it reads
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extract configured root types from the dataset registry instead of from each dataset’s own distribution.
A dataset’s description is governed by a different application profile from the objects it contains, and it lives in the register – nothing obliges a publisher to describe its own dataset inside its dump. So a
Datasetroot type extracted from the distribution yields documents for the few publishers that self-describe and nothing for the rest, leaving every reference resolved through it unlabelled.Same CONSTRUCT generator, same framing, same projection, same writers – only the source differs. The extraction generator, the projection, the writers and
SearchTypeare untouched.Changes
@lde/pipelineStageOptions.sourceFor– a per-stage source override,(dataset, distribution) => Distribution. One hook, applied once at the top ofStage.run, so the item selector and every reader are handed the same substitute: a stage that reads elsewhere must select there too, or its roots and its quads come from different sources.SparqlItemSelectornow honoursDistribution.namedGraph, which only the reader applied. Without it a registry-sourced stage selects the whole catalogue instead of the dataset in hand. The graph scope and the page window are now derived perselect()call rather than mutated on shared state, so concurrent selections over different datasets cannot see each other’s.@lde/search-pipelineregistrySource(endpoint)– asourceForthat reads a fixed registry endpoint, scoped to the graph the dataset in hand names.SearchStageType.sourceForpassthrough, andsearchIndexerPipeline’sregistryTypes: { endpoint, names }for the convenience path. A name the schema does not declare throws at wiring time.@lde/search-indexerREGISTRY_ROOT_TYPES(whitespace- or comma-separated type names) alongsideREGISTRY_ENDPOINT. Unset means every type reads the distribution, exactly as before.Why graph scoping carries the design
A register holds every registration, so an unscoped registry stage would re-index the whole catalogue once per dataset processed. Scoped to the dataset’s graph, one pass sees exactly one registration.
Verified against the live NDE Dataset Register (2731 registrations):
FROM <dataset-iri>is the existingDistribution.namedGraphmechanism;foaf:nameinside it, and 2730/2731 type the publisherfoaf:Organizationthere too – soselectByClassworks unchanged for aPublisherroot type, and the publisher hop resolves from the declared paths with no CBD rule or predicate list;dcat:Datasetin the dataset’s graph andschema:Datasetin the registrations graph, so a graph-scopeddcat:Datasetselector cannot reach the registrations graph at all – and excludes the 775 subjects that areschema:Datasetonly (registrations never crawled), which a schema.org-rooted selector would ship as content-free documents.Routing stays in configuration rather than in the schema: a
SearchTypeis defined by itsclass, not by where its triples come from, so the same declaration serves a deployment that sources it differently.Fix #695